#define sensorNum 5
#define pinLED 9
#define pinSOLENOID 3
#define pinMODE 13
#define recordingThreshold 4000
#define dataThreshold 100
#define timeThreshold 100
#define allDrumConflictTimeThreshold 100
#define knockSaveLength 100

#define NORMAL 0
#define FAST 1
#define SLOW 2

#include "DataProcessing.h"

typedef struct {
  unsigned int preTime;
  unsigned int nowTime;
} 
Timer;

DataProcessing data[sensorNum]; 
int readIn[sensorNum] = {
  0,1,2,3,4};

Timer allDrumConflictTime;
Timer ledTime;
Timer recordingTime;
Timer repeatTime;
Timer solenoidTime;
Timer modeTime;

int preCounter=0;
int nowCounter=0;

int save = 0;
int repeat = 1;

unsigned int tempKnockTime;
boolean firstKnock = false;

int ledValue = 255;

int knockIntervalCounter = 0;
int repeatIntervalCounter = 0;

int knockInterval[2][knockSaveLength]={
  -1};

boolean ifStartKnock = false;
boolean ifStartRepeat = false;
boolean ifModeSwitch = false;
boolean ifModeSwitchChanged = false;
int modeSwitch = 0;

int currentMode;

void setup()
{
  Serial.begin(9600);
  pinMode(pinLED,OUTPUT);
  pinMode(pinSOLENOID, OUTPUT);
  pinMode(pinMODE, INPUT);

  allDrumConflictTime.preTime = 0;
  allDrumConflictTime.nowTime = 0;

  ledTime.preTime = 0;
  ledTime.nowTime = 0;

  recordingTime.preTime = 0;
  recordingTime.nowTime = 0;

  repeatTime.preTime = 0;
  repeatTime.nowTime = 0;

  solenoidTime.preTime = 0;
  solenoidTime.nowTime = 0;

  modeTime.preTime = 0;
  modeTime.nowTime = 0;
}

int SwitchMode()
{
  modeSwitch = digitalRead(pinMODE);
  if(modeSwitch == 1 && !ifModeSwitch)
  {
    modeTime.nowTime = millis();
    modeTime.preTime = modeTime.nowTime;
    ifModeSwitch = true;
  }
  if(modeSwitch == 1 && ifModeSwitch && modeTime.nowTime-modeTime.preTime > 200 && !ifModeSwitchChanged)
  {

    ifModeSwitchChanged = true;
    if(currentMode<2)
      currentMode++;
    else
      currentMode = 0;
  }
  if(modeSwitch == 1)
  {
    int temp = modeTime.nowTime-modeTime.preTime;
    
    if(ifModeSwitchChanged && temp<1000)            // light up the led graduately
      analogWrite(pinLED, map(temp,201,1000,0,255));
    else if(ifModeSwitchChanged && temp>=1000)
      digitalWrite(pinLED,HIGH);
      
    modeTime.nowTime = millis();
  }
  if(modeSwitch == 0 && ifModeSwitchChanged)
  {
    ifModeSwitch = false;
    ifModeSwitchChanged = false;
    digitalWrite(pinLED,LOW);
    delay(500);
    switch (currentMode)
    {
    case NORMAL:
      BlinkLEDWithDelay(1);
      break;
    case FAST:
      BlinkLEDWithDelay(2);
      break;
    case SLOW:
      BlinkLEDWithDelay(3);
      break;
    }

  }  
  return currentMode;
}


void loop()
{
  if(!IsStartKnock())
    SwitchMode();  //change mode

    solenoidTime.nowTime = millis();

  if(solenoidTime.nowTime-solenoidTime.preTime>20)
    digitalWrite(pinSOLENOID,LOW);

  if(IsStartKnock())  //if recording knocks
  {
    recordingTime.nowTime = millis();
    ledTime.nowTime = recordingTime.nowTime;
    if(recordingTime.nowTime - recordingTime.preTime < recordingThreshold)
    {
      if(ledTime.nowTime-ledTime.preTime>14 && ledValue>=0)  //dim the led light
      {
        ledValue--;
        ledTime.preTime = ledTime.nowTime;
      }
      analogWrite(pinLED,ledValue); 
    }
    else
    {
      Serial.println(millis());
      ResetKnock();  //ready for record next round of knocks
      StartRepeat();  //start repeat the current knocks
      digitalWrite(pinLED,LOW);
    }
  }

  if(IsStartRepeat())  // if repeating
  {
    if(repeatTime.nowTime-repeatTime.preTime >= knockInterval[repeat][repeatIntervalCounter] && knockInterval[repeat][repeatIntervalCounter]!=-1)  //repeat the [repeatIntervalCounter] knock
    {
      //Serial.println("Repeat Knock");
      solenoidTime.nowTime = millis();
      solenoidTime.preTime = solenoidTime.nowTime;

      digitalWrite(pinSOLENOID, HIGH);  
      repeatIntervalCounter++;
      repeatTime.nowTime = millis();
      repeatTime.preTime = repeatTime.nowTime;
      //Serial.println("FFF");
    }    
    repeatTime.nowTime = millis();
  }

  for(int i=0; i<sensorNum; i++)  //read data from analog pins
  {
    data[i].smoothData(analogRead(readIn[i]));  //process each of the data
  }

  for(int i=0; i<sensorNum; i++)
  {
    if(data[i].compareData(dataThreshold) && data[i].compareTime(timeThreshold) && !data[i].getCondition())
    {
      data[i].setCondition(true);
      data[i].setPreTime();

      allDrumConflictTime.nowTime = millis();
      if(allDrumConflictTime.nowTime-allDrumConflictTime.preTime > allDrumConflictTimeThreshold)
      {
        unsigned int tempNowTime = millis();
        if(!ifStartKnock)
          StartKnock();  //turn on the drum recording

        if(!firstKnock)
        {
          firstKnock = true;
          knockInterval[save][knockIntervalCounter] = 0;
          //Serial.println(millis());
        }   
        else
        {
          knockInterval[save][knockIntervalCounter] = tempNowTime-tempKnockTime;   
        }
        tempKnockTime = tempNowTime;      

        knockIntervalCounter++;        
        allDrumConflictTime.preTime = allDrumConflictTime.nowTime;

      }
      break;

    }
    else if(data[i].getCondition() && !data[i].compareData(dataThreshold))
    {
      data[i].setCondition(false);
    }

  }


}

void BlinkLEDWithDelay(int count)
{
  for(int i=0;i<count;i++)
  {
    digitalWrite(pinLED,HIGH);
    delay(150);
    digitalWrite(pinLED,LOW);
    delay(150);
  }
}
boolean IsStartKnock()
{
  return ifStartKnock;
}

boolean IsStartRepeat()
{
  return ifStartRepeat;
}

void StartKnock()
{
  ifStartKnock = true;
  firstKnock = false;
  recordingTime.nowTime = millis();
  knockIntervalCounter = 0;
  recordingTime.preTime = recordingTime.nowTime;
  ledValue = 255;
  //  for(int i=0;i<knockSaveLength;i++)
  //    knockInterval[save][i] = -1;

}

void ResetKnock()
{
  ifStartKnock = false;
  knockInterval[save][knockIntervalCounter] = -1;
  Serial.println("Reset");
  SwitchSaveReplaySign();
}

void SwitchSaveReplaySign()
{
  if(save == 0)
  {
    save = 1;
    repeat = 0;
  }
  else
  {
    save = 0;
    repeat = 1;
  }
}

void StartRepeat()
{
  ifStartRepeat = true;
  repeatIntervalCounter = 0;
  repeatTime.preTime = 0;
  repeatTime.nowTime = 0;
  solenoidTime.preTime = 0;
  solenoidTime.nowTime = 0;
  int tempTotal = 0;
  int tempCount = 0;
  //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////  
  switch(currentMode)
  {
  case NORMAL:
    break;
  case FAST:
    for(int i=0; i<knockIntervalCounter;i++)
      tempTotal+=knockInterval[repeat][i];
    for(int i=0; i<knockIntervalCounter; i++)
    {
      if(i!=0)
        knockInterval[repeat][i+knockIntervalCounter] = knockInterval[repeat][i]/2;
      else
        knockInterval[repeat][i+knockIntervalCounter] = knockInterval[repeat][i]/2+(recordingThreshold-tempTotal)/2;

      knockInterval[repeat][i] /= 2;
    }
    knockIntervalCounter *= 2;
    knockInterval[repeat][knockIntervalCounter] = -1;

    break;
  case SLOW:
    for(int i=0;i<knockIntervalCounter; i++)
    {
      knockInterval[repeat][i] *= 2;
      tempTotal+=knockInterval[repeat][i];
      tempCount++;
      if(tempTotal>recordingThreshold)
        break;
    }
    knockInterval[repeat][tempCount] = -1;
    break;
  }
  /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////  
}






